/* Copyright (c) 2009, Michael Patraw
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * The name of Michael Patraw may not be used to endorse or promote
 *       products derived from this software without specific prior written
 *       permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Michael Patraw ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Michael Patraw BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
 
/**
 * A generic stack.
 */
#ifndef AT_CONTAINER_STACK_H
#define AT_CONTAINER_STACK_H

#include <at/util/ints.h>
#include <at/container/general.h>

#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

/******************************************************************************\
\******************************************************************************/

/**
 * The dynamic array.
 */
typedef struct atStack_T atStack_T;


/******************************************************************************\
\******************************************************************************/

/**
 * Creates a new stack structure.
 *
 * \return A new stack.
 */
atStack_T *atStack_Malloc(void);



/**
 * Frees a stack structure.
 *
 * \param s The stack.
 * \return Nothing.
 */
void atStack_Free(atStack_T *s);



/**
 * Pops some data from the stack.
 *
 * \param s The stack.
 * \return What was on top.
 */
void *atStack_Pop(atStack_T *s);



/**
 * Pushes some data onto the stack.
 *
 * \param s The stack.
 * \param d The data.
 * \return Nothing.
 */
void atStack_Push(atStack_T *s, void *d);



/**
 * Checks the size of the stack.
 *
 * \param s The stack.
 * \return The size of the stack.
 */
atuint atStack_Size(atStack_T *s);



/**
 * Finds something in the stack.
 *
 * \param s The stack.
 * \param d The object to search for.
 * \param cmp The comparison function.
 * \return The location or -1 if it couldn't find.
 */
atint atStack_Find(atStack_T *s, void *d, Container_CmpF cmp);

/******************************************************************************\
\******************************************************************************/

/*
 * Possible type-safer variations of the above. But slightly slower.
 */
#define DECLARE_ATSTACK(name, type) \
typedef struct name name; \
typedef int (*name##_CmpF) (type a, type b); \
name *name##_Malloc(void); \
void name##_Free(name *s); \
type name##_Pop(name *s); \
void name##_Push(name *s, type d); \
int name##_Size(name *s); \
int name##_Find(name *s, type d, name##_CmpF cmp);



#define DEFINE_ATSTACK(name, type) \
struct name { \
    atStack_T *stack; \
}; \
name##_CmpF _##name##_cmpf = NULL; \
int _##name##_CmpF(void *a, void *b) \
{ \
    return _##name##_cmpf(*(type *)a, *(type *)b); \
} \
name *name##_Malloc(void) \
{ \
    name *n = (name *)malloc(sizeof(name)); \
    if (!n) return NULL; \
    n->stack = atStack_Malloc(); \
    if (!n->stack) { free(n); return NULL; } \
    return n; \
} \
void name##_Free(name *s) \
{ \
    while (atStack_Size(s->stack) > 0) \
        free(atStack_Pop(s->stack)); \
    atStack_Free(s->stack); \
    free(s); \
} \
type name##_Pop(name *s) \
{ \
    type t; \
    void *ret = atStack_Pop(s->stack); \
    t = *(type *)ret; \
    free(ret); \
    return t; \
} \
void name##_Push(name *s, type d) \
{ \
    type *t = (type *)malloc(sizeof(type)); \
    if (!t) return; \
    *t = d; \
    atStack_Push(s->stack, t); \
} \
int name##_Size(name *s) \
{ \
    return atStack_Size(s->stack); \
} \
int name##_Find(name *s, type d, name##_CmpF cmp) \
{ \
    type *t = (type *)malloc(sizeof(type)); \
    assert(t || !"Failed to allocated memory"); \
    *t = d; \
    _##name##_cmpf = cmp; \
    return atStack_Find(s->stack, t, _##name##_CmpF); \
    free(t); \
}

#ifdef __cplusplus
}
#endif

#endif

